/** 
 * Title: ast.dump.h
 * Description: Dump ast as text
 * Copyright: Copyright (c) 2022
 * Company: Beijing University of Posts and Telecommunications
 * @author 2019211410 王敏行 2019211416 邹宇江
 * @date 2022/04/10 16:32 GMT+08:00
 * @version 0.2.0-beta
 */

#ifndef _AST_DUMP_H
#define _AST_DUMP_H

#include <iostream>
#include "../ast/ast.h"

// #define ACCEPT(ast) {                               \
//     if (nullptr != ast)                             \
//         ast->accept(this);                          \
//     else                                            \
//         os << in() << "nullptr" << std::endl;       \
// }                                                   \

class ASTDump : public Visitor {
private:
    std::ostream &os;
    int depth;
    std::string in() { return std::string(depth * 4, ' '); }
    inline void ACCEPT(AST* ast) {
        if (nullptr != ast) { ast->accept(this); }
        else { os << in() << "nullptr" << std::endl; }
    }

public:
    ASTDump() : os(std::cout), depth(-1) {}
    ASTDump(std::ostream &os) : os(os), depth(-1) {}

    void* visit(AST* node) final {
        depth++;
        os << in() << "AST" << std::endl;
        os << in() << "| ln:" << node->get_ln() << std::endl;
        os << in() << "| col:" << node->get_col() << std::endl;
        depth--;
        return nullptr;
    }

    void* visit(ProgramAST* node) final {
        depth++;
        os << in() << "Program" << std::endl;
        os << in() << "| ProgramHeading: " << std::endl;
        ACCEPT(node->get_head());
        os << in() << "| ProgramBlock: " << std::endl;
        ACCEPT(node->get_block());
        depth--;
        return nullptr;
    }

    void* visit(ProgramHeadingAST* node) final {
        depth++;
        os << in() << "ProgramHeading" << std::endl;
        os << in() << "| name: " << node->get_name() << std::endl;
        os << in() << "| ProgramParametersAST: " << std::endl;
        ACCEPT(node->get_params());
        depth--;
        return nullptr;
    }

    void* visit(ProgramBlockAST* node) final {
        depth++;
        os << in() << "ProgramBlockAST" << std::endl;
        os << in() << "| block: " << std::endl;
        ACCEPT(node->get_block());
        depth--;
        return nullptr;
    }

    void* visit(BlockAST* node) final {
        depth++;
        os << in() << "BlockAST" << std::endl;
        os << in() << "| constList: " << std::endl;
        ACCEPT(node->get_const());
        os << in() << "| typeList: " << std::endl;
        ACCEPT(node->get_type());
        os << in() << "| varList: " << std::endl;
        ACCEPT(node->get_var());
        os << in() << "| proFuncList: " << std::endl;
        ACCEPT(node->get_proc());
        os << in() << "| statement: " << std::endl;
        ACCEPT(node->get_stmt());
        depth--;
        return nullptr;
    }

    void* visit(ProgramParametersAST* node) final {
        depth++;
        os << in() << "ProgramParameterAST" << std::endl;
        os << in() << "| idList: " << std::endl;
        ACCEPT(node->get_id_list());
        depth--;
        return nullptr;
    }

    void* visit(IdentifierListAST* node) final {
        depth++;
        os << in() << "IdentifierListAST" << std::endl;
        os << in() << "| idList: ";
        std::vector<std::string> idList = node->get_id_list();
        for (auto iter = idList.begin(); iter != idList.end(); iter++) {
            if (iter != idList.begin()) {
                os << ", ";
            }
            os << (*iter);
        }
        os << std::endl;
        depth--;
        return nullptr;
    }

    void* visit(ConstantDefinitionListAST* node) final {
        depth++;
        os << in() << "ConstantDefinitionListAST" << std::endl;
        os << in() << "| constList: " << std::endl;
        std::vector<ConstantDefinitionAST*> constList = node->get_const_list();
        for (auto iter = constList.begin(); iter != constList.end(); iter++) {
            if (iter != constList.begin()) {
                // os << ", ";
            }
            ACCEPT((*iter));
        }
        depth--;
        return nullptr;
    }

    void* visit(ConstantDefinitionAST* node) final {
        depth++;
        os << in() << "ConstantDefinitionAST" << std::endl;
        os << in() << "| id: " << node->get_id() << std::endl;
        os << in() << "| value: " << std::endl;
        ACCEPT(node->get_val());
        depth--;
        return nullptr;
    }

    void* visit(TypeDefinitionListAST* node) final {
        depth++;
        os << in() << "TypeDefinitionListAST" << std::endl;
        os << in() << "| typeList: " << std::endl;
        std::vector<TypeDefinitionAST*> typeList = node->get_type_list();
        for (auto iter = typeList.begin(); iter != typeList.end(); iter++) {
            if (iter != typeList.begin()) {
                // os << ", ";
            }
            ACCEPT((*iter));
        }
        depth--;
        return nullptr;
    }

    void* visit(TypeDefinitionAST* node) final {
        depth++;
        os << in() << "TypeDefinitionAST" << std::endl;
        os << in() << "| id: " << node->get_id() << std::endl;
        os << in() << "| type: " << std::endl;
        ACCEPT(node->get_type());
        depth--;
        return nullptr;
    }

    void* visit(VariableDeclarationListAST* node) final {
        depth++;
        os << in() << "VariableDefinitionListAST" << std::endl;
        os << in() << "| varList: " << std::endl;
        std::vector<VariableDeclarationAST*> varList = node->get_var_list();
        for (auto iter = varList.begin(); iter != varList.end(); iter++) {
            if (iter != varList.begin()){
                // os << ", ";
            }
            ACCEPT((*iter));
        }
        depth--;
        return nullptr;
    }

    void* visit(VariableDeclarationAST* node) final {
        depth++;
        os << in() << "VariableDeclarationAST" << std::endl;
        os << in() << "| type: " << std::endl;
        ACCEPT(node->get_type());
        os << in() << "| idList: " << std::endl;
        ACCEPT(node->get_id_list());
        depth--;
        return nullptr;
    }

    void* visit(ProcedureFunctionDeclarationListAST* node) final {
        depth++;
        os << in() << "ProcedureFunctionDeclarationListAST" << std::endl;
        os << in() << "| subProList: " << std::endl;
        std::vector<SubprogramDeclarationAST*> subProgList = node->get_sub_list();
        for (auto iter = subProgList.begin(); iter != subProgList.end(); iter++) {
            if (iter != subProgList.begin()) {
                // os << ", ";
            }
            ACCEPT((*iter));
        }
        depth--;
        return nullptr;
    }

    void* visit(SubprogramDeclarationAST* node) final {
        depth++;
        os << in() << "SubProgramDeclarationAST" << std::endl;
        os << in() << "| type: ";
        switch (node->type()) {
            depth++;
        case SubprogramDeclarationAST::Type::FUNCTION:
            os << "FUNCTION" << std::endl;
            break;
        case SubprogramDeclarationAST::Type::PROCEDURE:
            os << "PROCEDURE" << std::endl;
            break;
        default:
            break;
            depth--;
        }
        depth--;
        return nullptr;
    }

    void* visit(ProcedureDeclarationAST* node) final {
        depth++;
        os << in() << "ProcedureDeclarationAST" << std::endl;
        os << in() << "| ProcHead: " << std::endl;
        ACCEPT(node->get_head());
        os << in() << "| ProcBody: " << std::endl;
        ACCEPT(node->get_body());
        depth--;
        return nullptr;
    }

    void* visit(ProcedureHeadingAST* node) final {
        depth++;
        os << in() << "ProcedureHeadingAST" << std::endl;
        os << in() << "| name: " << node->get_name() << std::endl;
        os << in() << "| params: " << std::endl;
        ACCEPT(node->get_params());
        depth--;
        return nullptr;
    }

    void* visit(ProcedureBodyAST* node) final {
        depth++;
        os << in() << "ProcedureBodyAST" << std::endl;
        os << in() << "| block: " << std::endl;
        ACCEPT(node->get_block());
        depth--;
        return nullptr;
    }

    void* visit(TypeDenoterAST* node) final {
        depth++;
        os << in() << "TypeDenototerAST" << std::endl;
        os << in() << "| type: ";
        switch (node->type()) {
            depth++;
        case TypeDenoterAST::Type::ARRAY:
            os << "ARRAY" << std::endl;
            break;
        case TypeDenoterAST::Type::ENUMERATED:
            os << "ENUMERATED" << std::endl;
            break;
        case TypeDenoterAST::Type::FILE:
            os << "FILE" << std::endl;
            break;
        case TypeDenoterAST::Type::IDENTIFIER:
            os << "IDENTIFIER" << std::endl;
            break;
        case TypeDenoterAST::Type::POINTER:
            os << "POINTER" << std::endl;
            break;
        case TypeDenoterAST::Type::RECORD:
            os << "RECORD" << std::endl;
            break;
        case TypeDenoterAST::Type::SET:
            os << "SET" << std::endl;
            break;
        case TypeDenoterAST::Type::SIMPLE:
            os << "SIMPLE" << std::endl;
            break;
        case TypeDenoterAST::Type::SUBRANGE:
            os << "SUBRANGE" << std::endl;
            break;
        default:
            break;
            depth--;
        }
        depth--;
        return nullptr;
    }

    void* visit(FunctionDeclarationAST* node) final {
        depth++;
        os << in() << "FunctionDeclarationAST" << std::endl;
        os << in() << "| FuncHead: " << std::endl;
        ACCEPT(node->get_head());
        os << in() << "| FuncBody: " << std::endl;
        ACCEPT(node->get_body());
        depth--;
        return nullptr;
    }

    void* visit(FunctionHeadingAST* node) final {
        depth++;
        os << in() << "FunctionHeadingAST" << std::endl;
        os << in() << "| name: " << node->get_name() <<std::endl;
        os << in() << "| retType: " << std::endl;
        ACCEPT(node->get_ret_type());
        os << in() << "| params: " << std::endl;
        ACCEPT(node->get_params());
        depth--;
        return nullptr;
    }

    void* visit(FunctionBodyAST* node) final {
        depth++;
        os << in() << "FunctionBodyAST" << std::endl;
        os << in() << "| body: " << std::endl;
        ACCEPT(node->get_block());
        depth--;
        return nullptr;
    }

    void* visit(FormalParameterListAST* node) final {
        depth++;
        os << in() << "FormalParameterListAST" << std::endl;
        os << in() << "| params: " << std::endl;
        std::vector<FormalParameterSectionAST*> params = node->get_params();
        for (auto iter = params.begin(); iter != params.end(); iter++) {
            if (iter != params.begin()){
                // os << ", ";
            }
            ACCEPT((*iter));
        }
        depth--;
        return nullptr;
    }

    void* visit(FormalParameterSectionAST* node) final {
        depth++;
        os << in() << "FromalParameterSectionAST" << std::endl;
        os << in() << "| type: " << std::endl;
        switch (node->type()) {
            depth++;
        case FormalParameterSectionAST::Type::VALUE:
            os << "VALUE" << std::endl;
            break;
        case FormalParameterSectionAST::Type::VARIABLE:
            os << "VARIABLE" << std::endl;
            break;
        default:
            break;
            depth--;
        }
        depth--;
        return nullptr;
    }

    void* visit(ValueParameterSpecificationAST* node) final {
        depth++;
        os << in() << "ValueParameterSpecificationAST" << std::endl;
        os << in() << "| idList: " << std::endl;
        ACCEPT(node->get_id_list());
        os << in() << "| type: " << std::endl;
        ACCEPT(node->get_type());
        depth--;
        return nullptr;
    }

    void* visit(VariableParameterSpecificationAST* node) final {
        depth++;
        os << in() << "VariableParameterSpecificationAST" << std::endl;
        os << in() << "| idList: " << std::endl;
        ACCEPT(node->get_id_list());
        os << in() << "| type: " << std::endl;
        ACCEPT(node->get_type());
        depth--;
        return nullptr;
    }

    void* visit(ConstantAST* node) final {
        depth++;
        os << in() << "ConstantAST" << std::endl;
        os << in() << "| sign: " << node->sign() <<std::endl;
        os << in() << "| type: ";
        switch (node->type()) {
            depth++;
        case ConstantAST::Type::BOOLEAN:
            os << "BOOLEAN" << std::endl;
            break;
        case ConstantAST::Type::CHARACTER:
            os << "CHARACTER" << std::endl;
            break;
        case ConstantAST::Type::IDENTIFIER:
            os << "IDENTIFIER" << std::endl;
            break;
        case ConstantAST::Type::INTEGER:
            os << "INTERGER" << std::endl;
            break;
        case ConstantAST::Type::REAL:
            os << "REAL" << std::endl;
            break;
        case ConstantAST::Type::STRING:
            os << "STRING" << std::endl;
            break;
        default:
            break;
            depth--;
        }
        os << in() << "| value: " << node->value() << std::endl;
        depth--;
        return nullptr;
    }

    void* visit(TypeIdentifierAST* node) final {
        depth++;
        os << in() << "TypeIdentifierAST" << std::endl;
        os << in() << "| id: " << node->get_id() << std::endl;
        depth--;
        return nullptr;
    }

    void* visit(SimpleTypeIdentifierAST* node) final {
        depth++;
        os << in() << "SimpleTypeIdentifierAST" << std::endl;
        os << in() << "| type: ";
        switch (node->std_type()) {
            depth++;
        case SimpleTypeIdentifierAST::StandardType::BOOLEAN:
            os << "BOOLEAN" << std::endl;
            break;
        case SimpleTypeIdentifierAST::StandardType::CHARACTER:
            os << "CHARACTER" << std::endl;
            break;
        case SimpleTypeIdentifierAST::StandardType::INTEGER:
            os << "INTERGER" << std::endl;
            break;
        case SimpleTypeIdentifierAST::StandardType::REAL:
            os << "REAL" << std::endl;
            break;
        case SimpleTypeIdentifierAST::StandardType::STRING:
            os << "STRING" << std::endl;
            break;
        default:
            break;
            depth--;
        }
        depth--;
        return nullptr;
    }

    void* visit(ArrayTypeAST* node) final {
        depth++;
        os << in() << "ArrayTypeAST" << std::endl;
        os << in() << "| type: " << std::endl;
        ACCEPT(node->get_type());
        os << in() << "| perList: " << std::endl;
        ACCEPT(node->get_per_list());
        depth--;
        return nullptr;
    }

    void* visit(PeriodListAST* node) final {
        depth++;
        os << in() << "PeriodListAST" << std::endl;
        os << in() << "| perList: " << std::endl;
        std::vector<PeriodAST*> perList = node->get_per_list();
        for (auto iter = perList.begin(); iter != perList.end(); iter++) {
            if (iter != perList.begin()) {
                // os << ", ";
            }
            ACCEPT((*iter));
        }
        depth--;
        return nullptr;
    }

    void* visit(PeriodAST* node) final {
        depth++;
        os << in() << "PeriodAST" << std::endl;
        os << in() << "| begin: " << std::endl;
        ACCEPT(node->get_beg());
        os << in() << "| end: " << std::endl;
        ACCEPT(node->get_end());
        depth--;
        return nullptr;
    }

    void* visit(RecordTypeAST* node) final {
        depth++;
        os << in() << "RecordTypeAST" << std::endl;
        os << in() << "| fieldlist: " << std::endl;
        ACCEPT(node->get_field_list());
        depth--;
        return nullptr;
    }

    void* visit(FieldListAST* node) final {
        depth++;
        os << in() << "FieldListAST" << std::endl;
        os << in() << "| fieldList: " << std::endl;
        std::vector<FieldAST*> fieldList = node->get_field_list();
        for (auto iter = fieldList.begin(); iter != fieldList.end(); iter++) {
            if (iter != fieldList.begin()) {
                // os << ", ";
            }
            ACCEPT((*iter));
        }
        depth--;
        return nullptr;
    }

    void* visit(FieldAST* node) final {
        depth++;
        os << in() << "FieldAST" << std::endl;
        os << in() << "| type: " << std::endl;
        ACCEPT(node->get_type());
        os << in() << "| idList: " << std::endl;
        ACCEPT(node->get_id_list());
        depth--;
        return nullptr;
    }

    void* visit(StatementAST* node) final {
        depth++;
        os << in() << "StatementAST" << std::endl;
        os << in() << "| type: ";
        switch (node->type()) {
            depth++;
        case StatementAST::StmtType::ASSIGN:
            os << "ASSIGN" << std::endl;
            break;
        case StatementAST::StmtType::CASE:
            os << "CASE" << std::endl;
            break;
        case StatementAST::StmtType::COMPOUND:
            os << "COMPOUND" << std::endl;
            break;
        case StatementAST::StmtType::EMPTY:
            os << "EMPTY" << std::endl;
            break;
        case StatementAST::StmtType::FOR:
            os << "FOR" << std::endl;
            break;
        case StatementAST::StmtType::IF:
            os << "IF" << std::endl;
            break;
        case StatementAST::StmtType::PROCEDURE:
            os << "PROCEDURE" << std::endl;
            break;
        case StatementAST::StmtType::REPEAT:
            os << "REPEAT" << std::endl;
            break;
        case StatementAST::StmtType::WHILE:
            os << "WHILE" << std::endl;
            break;
        default:
            break;
            depth--;
        } 
        depth--;
        return nullptr;
    }

    void* visit(ProcedureStatementAST* node) final {
        depth++;
        os << in() << "ProcedureStatementAST" << std::endl;
        os << in() << "| proName: " << node->get_pro_name() << std::endl;
        os << in() << "| paramList: " << std::endl;
        ACCEPT(node->get_param_list());
        depth--;
        return nullptr;
    }

    void* visit(IOProcedureStatementAST* node) final {
        depth++;
        os << in() << "IOProcedureStatementAST" << std::endl;
        depth--;
        return nullptr;
    }

    void* visit(ActualParameterListAST* node) final {
        depth++;
        os << in() << "ActualParameterListAST" << std::endl;
        os << in() << "| actualParamList: " << std::endl;
        std::vector<ActualParameterAST*> actualParamList = node->get_actual_param_list();
        for (auto iter = actualParamList.begin(); iter != actualParamList.end(); iter++) {
            if (iter != actualParamList.begin()) {
                // os << ", ";
            }
            ACCEPT((*iter));
        }
        depth--;
        return nullptr;
    }

    void* visit(ReadParameterListAST* node) final {
        depth++;
        os << in() << "ReadParameterListAST" << std::endl;
        depth--;
        return nullptr;
    }

    void* visit(WriteParameterList* node) final {
        depth++;
        os << in() << "WriteParameterListAST" << std::endl;
        depth--;
        return nullptr;
    }

    void* visit(ActualParameterAST* node) final {
        depth++;
        os << in() << "ActualParameterAST" << std::endl;
        os << in() << "| expression: " << std::endl;
        ACCEPT(node->get_expression());
        depth--;
        return nullptr;
    }

    void* visit(StatementPartAST* node) final {
        depth++;
        os << in() << "StatementPartAST" << std::endl;
        os << in() << "| compoundStatement: " << std::endl;
        ACCEPT(node->get_compound_statement());
        depth--;
        return nullptr;
    }

    void* visit(CompoundStatement* node) final {
        depth++;
        os << in() << "CompoudStatement" << std::endl;
        os << in() << "| statementList: " << std::endl;
        ACCEPT(node->get_statement_list());
        depth--;
        return nullptr;
    }

    void* visit(StatementSequenceAST* node) final {
        depth++;
        os << in() << "StatementSequenceAST" << std::endl;
        os << in() << "| statementList: " << std::endl;
        std::vector<StatementAST*> statementList = node->get_statement_list();
        for (auto iter = statementList.begin(); iter != statementList.end(); iter++) {
            if (iter != statementList.begin()) {
                // os<<", ";
            }
            ACCEPT((*iter));
        }
        depth--;
        return nullptr;
    }

    void* visit(EmptyStatementAST* node) final {
        depth++;
        os << in() << "EmptyStatementAST" << std::endl;
        depth--;
        return nullptr;
    }

    void* visit(AssignmentStatementAST* node) final {
        depth++;
        os << in() << "AssignmentSatementAST" << std::endl;
        os << in() << "| lhs: " << std::endl;
        ACCEPT(node->get_lhs());
        os << in() << "| rhs: " << std::endl;
        ACCEPT(node->get_rhs());
        depth--;
        return nullptr;
    }

    void* visit(IfStatementAST* node) final {
        depth++;
        os << in() << "IfStatementAST" << std::endl;
        os << in() << "| expression: " << std::endl;
        ACCEPT(node->get_expression());
        os << in() << "| thenStatement: " << std::endl;
        ACCEPT(node->get_then_statement());
        if (node->get_else_statement() != nullptr) {
            depth++;
            os << in() << "| elseStatement: " << std::endl;
            ACCEPT(node->get_else_statement());
            depth--;
        }
        depth--;
        return nullptr;
    }

    void* visit(CaseStatementAST* node) final {
        depth++;
        os << in() << "CaseStatementAST" << std::endl;
        os << in() << "| expression: " << std::endl;
        ACCEPT(node->get_expression());
        os << in() << "| caseBody: " << std::endl;
        ACCEPT(node->get_case_body());
        depth--;
        return nullptr;
    }
    
    void* visit(RepeatStatementAST* node) final {
        depth++;
        os << in() << "RepeatStatementAST" << std::endl;
        os << in() << "| expression: " << std::endl;
        ACCEPT(node->get_expression());
        os << in() << "| statementList: " << std::endl;
        ACCEPT(node->get_statement_list());
        depth--;
        return nullptr;
    }

    void* visit(WhileStatementAST* node) final {
        depth++;
        os << in() << "WhileStatementAST" << std::endl;
        os << in() << "| expression: " << std::endl;
        ACCEPT(node->get_expression());
        os << in() << "| satement: " << std::endl;
        ACCEPT(node->get_statement());
        depth--;
        return nullptr;
    }

    void* visit(ForStatementAST* node) final {
        depth++;
        os << in() << "ForStatementAST" << std::endl;
        os << in() << "| id: " << std::endl;
        ACCEPT(node->get_id());
        os << in() << "| iniValue: " << std::endl;
        ACCEPT(node->get_initial_value());
        os << in() << "| direction: ";
        switch (node->get_direction()) {
            depth++;
        case ForStatementAST::ForDirection::OP_DOWNTO:
            os << "DOWNTO" << std::endl;
            break;
        case ForStatementAST::ForDirection::OP_TO:
            os << "TO" << std::endl;
            break;
        default:
            break;
            depth--;
        }
        os << in() << "| endValue: " << std::endl;
        ACCEPT(node->get_endvalue());
        os << in() << "| statement: " << std::endl;
        ACCEPT(node->get_statement());
        depth--;
        return nullptr;
    }

    void* visit(CaseListElementListAST* node) final {
        depth++;
        os << in() << "CaseListElementListAST" << std::endl;
        os << in() << "| branchList: " << std::endl;
        std::vector<CaseListElementAST*> branchList = node->get_banch_list();
        for (auto iter = branchList.begin(); iter != branchList.end(); iter++) {
            if (iter != branchList.begin()) {
                // os << ", ";
            }
            ACCEPT((*iter));
        }
        depth--;
        return nullptr;
    }

    void* visit(CaseListElementAST* node) final {
        depth++;
        os << in() << "CaseListElementAST" << std::endl;
        os << in() << "| constList: " << std::endl;
        ACCEPT(node->get_const_list());
        os << in() << "| statement: " << std::endl;
        ACCEPT(node->get_statement());
        depth--;
        return nullptr;
    }

    void* visit(CaseConstantListAST* node) final {
        depth++;
        os << in() << "CaseConstantListAST" << std::endl;
        os << in() << "| constList: " << std::endl;
        std::vector<ConstantAST*> constList = node->get_const_list();
        for (auto iter = constList.begin(); iter != constList.end(); iter++) {
            if (iter != constList.begin()) {
                // os << ", ";
            }
            ACCEPT((*iter));
        }
        depth--;
        return nullptr;
    }

    void* visit(ExpressionAST* node) final {
        depth++;
        os << in() << "ExressionAST" <<std::endl;
        os << in() << "| tye: ";
        switch(node->type()) {
            depth++;
        case ExpressionAST::Type::BINARY:
            os << "BINARY" << std::endl;
            break;
        case ExpressionAST::Type::CONSTANT:
            os << "CONSTANT" << std::endl;
            break;
        case ExpressionAST::Type::FIELD:
            os << "FIELD" << std::endl;
            break;
        case ExpressionAST::Type::FUNCTION:
            os << "FUNTION" << std::endl;
            break;
        case ExpressionAST::Type::IDENTIFIER :
            os << "IDENTIFIER" << std::endl;
            break;
        case ExpressionAST::Type::INDEXED:
            os << "INDEXED" << std::endl;
            break;
        default:
            break;
            depth--;
        }
        depth--;
        return nullptr;
    }

    void* visit(UnsignedConstantAST* node) final {
        depth++;
        os << in() << "UnsignedConstantAST" << std::endl;
        os << in() << "| val: " << std::endl;
        ACCEPT(node->get_value());
        depth--;
        return nullptr;
    }

    void* visit(IdentifierAST* node) final {
        depth++;
        os << in() << "IdentifierAST" << std::endl;
        os << in() << "| name: " << node->get_name() << std::endl;;
        depth--;
        return nullptr;
    }

    void* visit(BinaryAST* node) final {
        depth++;
        os << in() << "BinaryAST" << std::endl;
        os << in() << "| op: ";
        switch (node->type())
        {
            depth++;
        case BinaryAST::OpType::OP_ADD:
            os << "ADD" <<std::endl;
            break;
        case BinaryAST::OpType::OP_SUB:
            os << "SUB" <<std::endl;
            break;
        case BinaryAST::OpType::OP_MUL:
            os << "MUL" <<std::endl;
            break;
        case BinaryAST::OpType::OP_DIV:
            os << "DIV" <<std::endl;
            break;
        case BinaryAST::OpType::OP_MOD:
            os << "MOD" <<std::endl;
            break;
        case BinaryAST::OpType::OP_INC:
            os << "INC" <<std::endl;
            break;
        case BinaryAST::OpType::OP_DEC:
            os << "DEC" <<std::endl;
            break;
        case BinaryAST::OpType::OP_EQ:
            os << "EQ" <<std::endl;
            break;
        case BinaryAST::OpType::OP_NE:
            os << "NE" <<std::endl;
            break;
        case BinaryAST::OpType::OP_LT:
            os << "LT" <<std::endl;
            break;
        case BinaryAST::OpType::OP_LE:
            os << "LE" <<std::endl;
            break;
        case BinaryAST::OpType::OP_GT:
            os << "GT" <<std::endl;
            break;
        case BinaryAST::OpType::OP_GE:
            os << "GE" <<std::endl;
            break;
        case BinaryAST::OpType::OP_AND:
            os << "AND" <<std::endl;
            break;
        case BinaryAST::OpType::OP_OR:
            os << "OR" <<std::endl;
            break;
        case BinaryAST::OpType::OP_NOT:
            os << "NOT" <<std::endl;
            break;
        default:
            break;
            depth--;
        }

        os << in() << "| lhs: " << std::endl;
        ACCEPT(node->get_lhs());
        os << in() << "| rhs: " << std::endl;
        ACCEPT(node->get_rhs());
        depth--;
        return nullptr;
    }

    void* visit(IndexedVariableAST* node) final {
        depth++;
        os << in() << "IndexedVariableAST" << std::endl;
        os << in() << "| arrayName: " << node->get_array_name() << std::endl;
        os << in() << "| indices: " << std::endl;
        ACCEPT(node->get_indices());
        depth--;
        return nullptr;
    }

    void* visit(IndexExpressionListAST* node) final {
        depth++;
        os << in() << "IndexExpressionListAST" << std::endl;
        os << in() << "| indices: " << std::endl;
        std::vector<IndexExpressionAST*> indices = node->get_indices();
        for (auto iter = indices.begin(); iter != indices.end(); iter++) {
            if (iter != indices.begin()) {
                // os << ", ";
            }
            ACCEPT((*iter));
        }
        depth--;
        return nullptr;
    }

    void* visit(IndexExpressionAST* node) final {
        depth++;
        os << in() << "IndexExpressionAST" << std::endl;
        os << in() << "| expression: " << std::endl;
        ACCEPT(node->get_expression());
        depth--;
        return nullptr;
    }

    void* visit(FieldDesignatorAST* node) final {
        depth++;
        os << in() << "FieldDesignatorAST" << std::endl;
        os << in() << "| recordName: " << node->get_record_name() << std::endl;
        os << in() << "| member: " << node->get_member() << std::endl;
        depth--;
        return nullptr;
    }

    void* visit(FunctionDesignatorAST* node) final {
        depth++;
        os << in() << "FunctionDesignatorAST" << std::endl;
        os << in() << "| funcName: " << node->get_func_name() << std::endl;
        os << in() << "| paramList: " << std::endl;
        ACCEPT(node->get_param_list());
        depth--;
        return nullptr;
    }
};

#endif
